import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import * as Minio from 'minio';
import { Readable } from 'stream';
import {
  FileMetadata,
  FileUploadDetails,
  MediaFileOptions,
  MediaType,
} from './dto/media-file-options.dto';
import { v4 } from 'uuid';

@Injectable()
export class MinioService {
  private minioClient: Minio.Client;

  constructor(private configService: ConfigService) {
    this.initClient();
  }

  private initClient() {
    this.minioClient = new Minio.Client({
      endPoint: this.configService.get('FILE_STORAGE_ENDPOINT'),
      port: +this.configService.get('FILE_STORAGE_PORT'),
      useSSL: this.configService.get('FILE_STORAGE_USE_SSL') === 'true',
      accessKey: this.configService.get('FILE_STORAGE_ACCESS_KEY'),
      secretKey: this.configService.get('FILE_STORAGE_SECRET_KEY'),
    });
  }

  async upload(
    file: Buffer | Readable,
    options: MediaFileOptions,
  ): Promise<FileMetadata> {
    const { bucket, ext, mimitype } = this.defineFileUploadDetails(
      options.type,
    );
    const key = `${v4()}.${ext}`;
    await this.minioClient.putObject(bucket, key, file, {
      'content-type': mimitype,
    });
    const fileStat: Minio.BucketItemStat = await this.minioClient.statObject(
      bucket,
      key,
    );

    // 校验参数
    const fileMetadata: FileMetadata = new FileMetadata(
      `${bucket}/${key}`,
      fileStat.size,
      fileStat.metaData['content-type'],
      ext,
    );
    return fileMetadata;
  }

  private defineFileUploadDetails(type: MediaType): FileUploadDetails {
    const desc: Record<MediaType, FileUploadDetails> = {
      [MediaType.IMAGE]: {
        bucket: this.configService.get('FILE_STORAGE_IMAGE_BUCKET'),
        ext: this.configService.get('FILE_STORAGE_IMAGE_EXT'),
        mimitype: this.configService.get('FILE_STORAGE_IMAGE_MIMETYPE'),
      },
    };

    const res = desc[type];
    if (!res) {
      throw new Error('Minio: unknown media type');
    }

    return res;
  }
}
